/* ************************************************** */
/* file io.cpp: contains most input/output functions  */
/*                                                    */
/* Copyright (c) 1990-98 by Donald R. Tveter          */
/*                                                    */
/* ************************************************** */

#include "cpn.h"

#if defined(UNIX) && defined(HOTKEYS)

#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>

#ifdef BSD
struct termios raw;    /* characteristics for hotkeys */
struct termios noraw;  /* original characteristics */
#elif defined NEXT
struct sgttyb raw;     /* characteristics for hotkeys */
struct sgttyb noraw;   /* original characteristics */
#else /* the System V solution */
struct termio raw;    /* characteristics for hotkeys */
struct termio noraw;  /* original characteristics */
#endif

char onechar[1];      /* the buffer for the hotkey character */
#endif

extern UNIT *locateunit();
int pg(FILE *,char *);
extern int printstats(FILE *,int,int,DATA S[2][2]);

extern char buffer[], outstr[], *inputfile, sysstr[];
extern char copyflag,*datafile,echo,informat;
extern char outformat, probtype, update, *wtfile, wtformat;
extern char ringbell, summary, runningflag, seednote;
extern char wtfilename[], trfiles[];
extern int bufferend, bufferptr, filestackptr, format[];
extern int filetimes[], wtfilecount, timestoread, maxiter;
extern FILE *data, *filestack[];
extern LAYER *last, *start;
extern int lastsave,pagesize,prevnpats;
extern int readerror, readingpattern, totaliter;
extern int printrate, saverate, lineno;
extern long iotime;
extern short nlayers;
extern WTTYPE unknown, toler;
extern WTTYPE initrange, kickrange, kicksize;
extern WTTYPE eta, eta2;
extern WTTYPE classoff, classon;
extern FILE *copy;
extern REAL toloverall;
extern DATA s[2][2];
extern int wtsinuse, wttotal;
extern char incrementseed;
extern unsigned seed;
extern SEEDNODE *seedstart;

#if defined(UNIX) && defined(HOTKEYS)

#ifdef BSD
void initraw()
{
 int i;
 tcgetattr(0,&noraw);
 raw.c_iflag = noraw.c_iflag;
 raw.c_oflag = noraw.c_oflag;
 raw.c_cflag = noraw.c_cflag;
 raw.c_lflag = noraw.c_lflag & ~ ECHO;
 raw.c_lflag = raw.c_lflag & ~ ICANON;
 for (i=0;i<NCCS;i++) raw.c_cc[i] = noraw.c_cc[i];
 raw.c_cc[VMIN] = 1;
 raw.c_cc[VTIME] = 0;
 raw.c_ispeed = noraw.c_ispeed;
 raw.c_ospeed = noraw.c_ospeed;
}

void setraw()
{
 tcsetattr(0,TCSANOW,&raw);
}

void setnoraw()
{
 tcsetattr(0,TCSANOW,&noraw);
}

#elif defined NEXT
void initraw()
{
 ioctl(0,TIOCGETD,&noraw);
 raw.sg_ispeed = noraw.sg_ispeed;
 raw.sg_ospeed = noraw.sg_ospeed;
 raw.sg_erase = noraw.sg_erase;
 raw.sg_kill = noraw.sg_kill;
 raw.sg_flags = noraw.sg_flags & 0000040;   /* turn on raw mode */
}

void setraw()
{
 ioctl(0,TIOCSETD,&raw);
}

void setnoraw()
{
 ioctl(0,TIOCSETD,&noraw);
}

#else /* System V code */
void initraw()
{
 int i;
 ioctl(0,TCGETA,&noraw);
 raw.c_iflag = noraw.c_iflag;
 raw.c_oflag = noraw.c_oflag;
 raw.c_cflag = noraw.c_cflag;
 raw.c_lflag = noraw.c_lflag & ~ 0x02;   /* hot key bit */
 raw.c_lflag = raw.c_lflag & ~ 010;      /* echo bit */
 raw.c_line = noraw.c_line;
 for (i=0;i<=7;i++) raw.c_cc[i] = noraw.c_cc[i];
 raw.c_cc[4] = 1;
}

void setraw()
{
 ioctl(0,TCSETA,&raw);
}

void setnoraw()
{
 ioctl(0,TCSETA,&noraw);
}
#endif /* end of the System V code */

int getch()
{
 int ch;
 setraw();
 do ch = read(0,onechar,1); while (ch != 1);
 setnoraw();
 return(onechar[0]);
}
#endif  /* end of the if defined(UNIX) ... code */

#ifdef INTEGER
short scale(REAL x)     /* returns x as a scaled 16-bit value */
{
 short s;

if (x > 31.999 || x < -32.0)
 {
  sprintf(outstr,"magnitude of %f is too large for the integer",x);
  pg(stdout,outstr);
  pg(stdout," representation\n");
  readerror = 1;
  if (x > 31.999) return(MAXSHORT); else return(MINSHORT);
 };
if (x > 0.0) s = x * 1024 + 0.5;
else s = x * 1024 - 0.5;
if (x != 0.0 && s == 0)
 {
  sprintf(outstr,"warning:  magnitude of %f is too small for",x);
  pg(stdout,outstr);
  pg(stdout," the integer representation\n");
  return(0);
 };
return(s);
}

REAL unscale(short x)  /* returns the REAL value of short x */
{return((REAL) x / 1024.0);}

REAL unscaleint(x)  /* returns the REAL value of INT32 x */
INT32 x;
{ return((REAL) x / 1024.0); }

#endif

int pushfile(char *filename,int timestoread)
{
FILE *file;

bufferptr = 0;
bufferend = 0;
buffer[0] = '\n';
file = fopen(filename,"r");
if (file == NULL)
 {
  sprintf(outstr,"cannot open:  %s\n",filename); pg(stdout,outstr);
  return(0);
 };
filestackptr = filestackptr + 1;
if (filestackptr > MAXFILES)
 {
  pg(stdout,"can't stack up any more files\n");
  filestackptr = filestackptr - 1;
  fclose(file);
  return(0);
 };
filestack[filestackptr] = file;
filetimes[filestackptr] = timestoread;
data = file;
return(1);
} 

void popfile()
{
bufferptr = 0;
bufferend = 0;
buffer[0] = '\n';
if (s[TOL][TRAIN].wrong == 0) /* break the "loop" */
   filetimes[filestackptr] = 1;
filetimes[filestackptr] = filetimes[filestackptr] - 1;
if (filetimes[filestackptr] > 0) rewind(data);
else if (filestackptr > 0)
 {
  fclose(data);
  filestackptr = filestackptr - 1;
 }
else pg(stdout,"\nunexpected EOF:  to quit the program, type q\n");
data = filestack[filestackptr];
}

int readch() /* returns the next character in the input buffer */
{ 
 int i, ch2;

if (bufferptr > bufferend) /* then read next line into buffer */
 {
#ifdef HOTKEYS
  if (data == stdin)
   {
    ch2 = getch();
    if (ch2 <= 26 || (ch2 >= 63 && ch2 <= 90)) /* HOTKEY characters */
     {
      if (ch2 == 2) buffer[0] = 'b';
      else if (ch2 == 9) buffer[0] = 'i';
      else if (ch2 == 18) buffer[0] = 'r';
      else buffer[0] = ch2;
      buffer[1] = '\n';
      buffer[2] = '\0';
      i = 1;
      printf("%s",buffer); 
      goto entryfromhotkeys;
     }
    else /* all other characters */
     {
      putchar(ch2);
      i = 0;
      buffer[i] = ch2;
#ifdef UNIX
      while (ch2 != '\n' && i < (BUFFSIZE-2))
#else
      while (ch2 != 13 && i < (BUFFSIZE-2))
#endif
       {
        ch2 = getch();
        if (ch2 == 8 && i >= 0)
         {
          i = i - 1;
          putchar(8);
         }
        else if (ch2 == 8) {/* do nothing */}
#ifdef UNIX
        else if (ch2 != '\n')
#else
        else if (ch2 != 13)
#endif
         {
          putchar(ch2);
          i = i + 1;
          buffer[i] = ch2;
         };
       };
      putchar('\n');
      i = i + 1;
      goto entryfromhotkeys;
     };
   }
  else
#endif
  ch2 = getc(data);
  if (ch2 == EOF) return(ch2);
  i = 0;
  while(ch2 != '\n' && i < (BUFFSIZE-2))
   {
#ifdef UNIX
    if (ch2 == 13) ch2 = ' '; /* turn a ctrl-M into a blank */
#endif
    if (ch2 == '\t') ch2 = ' ';
    buffer[i] = ch2;
    i = i + 1;
    ch2 = getc(data);
   };
entryfromhotkeys:
  if (i == (BUFFSIZE-2)) pg(stdout,"line too long\n");
  buffer[i] = '\n';
  buffer[i+1] = '\0';
  bufferend = i;
  bufferptr = 0;
  if (echo == '+') for(i = 0; i <= bufferend; i++) putchar(buffer[i]);
  if (copy && ((data == stdin) || (echo == '+')))
   for (i=0;i<=bufferend;i++) putc(buffer[i],copy);
 }
ch2 = buffer[bufferptr];
bufferptr = bufferptr + 1;
return(ch2);
}

void texterror()
{
pg(stdout,"unexpected text:  ");
pg(stdout,&buffer[bufferptr-1]);
bufferptr = bufferend;
}

char *readstr()
{
short i,start,end;
char *addr, *addr2;
i = bufferptr;
while (buffer[i] == ' ') i = i + 1;
start = i;
while (buffer[i] != ' ' && buffer[i] != '\n') i = i + 1;
end = i-1;
addr = (char *) malloc((int) end-start+2);
addr2 = addr;
for (i=start;i<=end;i++) *addr++ = buffer[i];
bufferptr = end + 1;
*addr = '\0';
return(addr2);
}

char *readline()
{
short i,start,end;
char *addr, *addr2;
i = bufferptr;
while (buffer[i] == ' ') i = i + 1;
start = i;
while (buffer[i] != '\r' && buffer[i] != '\n') i = i + 1;
end = i-1;
addr = (char *) malloc((int) end-start+2);
addr2 = addr;
for (i=start;i<=end;i++) *addr++ = buffer[i];
bufferptr = end + 1;
*addr = '\0';
return(addr2);
}

int scanfordigit()
{
int sign, ch2;

sign = 1;

restart:
do ch2 = readch(); while (ch2 == ' ' || ch2 == '\n');
if ((ch2 >= '0' && ch2 <= '9') || ch2 == '.')
 {
  bufferptr = bufferptr - 1;
  return(sign);
 };
          switch (ch2) {
case '}': readerror = 3;
          return(0);
case EOF: readerror = 2;
          return(0);
case '*': while (ch2 != '\n') ch2 = readch();
          goto restart;
case '-': sign = -sign;
          goto restart;
case 'h':
case 'x':
case 'X':
case 'H':
case 'd':
case 's': bufferptr = bufferptr - 1;
          return(0);
default:  readerror = 1;
          return(0);
          };
}

int readint(int min,int max,char command)
{
int sign, number, ch2;

readerror = 0;
sign = scanfordigit();
if (readerror)
 {
  if (readerror == 1) texterror();
  return(0);
 };
number = 0;
do ch2 = readch(); while (ch2 == ' ');
while (ch2 >= '0' && ch2 <= '9')
 {
  number = number * 10 + (ch2 - '0');
  ch2 = readch();
 };
bufferptr = bufferptr - 1;
number = sign * number;
if (number < min || number > max)
 {
  sprintf(outstr,"out of range value: %d",number); pg(stdout,outstr);
  if (data == stdin) pg(stdout,"\n");
  else {sprintf(outstr," in %c command\n",command); pg(stdout,outstr);};
  readerror = 1;
 };
return(number);
}

REAL readreal(int op,REAL min,int command)
{
REAL fractpart, divisor, intpart, sign;
double number;
int ch2, numread, i;

readerror = 0;
sign = (REAL) scanfordigit();
if (readerror || (sign == 0 && !readingpattern))
 {
  if (readerror == 1) texterror();
  return(0);
 };
ch2 = readch();
if (ch2 == 'x' && readingpattern) return(unscale(unknown));
i = 0;
while ((ch2 >= '0' && ch2 <='9') || ch2 == '+' || ch2 == '-' ||
       ch2 == 'e' || ch2 == 'E' || ch2 == '.')
 {
  outstr[i] = ch2;
  i = i+1;
  ch2 = readch();
 };
outstr[i] = 0;
bufferptr = bufferptr - 1;
numread = sscanf(outstr,"%lf",&number);
if (numread == 0)
 {
  pg(stdout,"bad real value\n");
  readerror = 1;
  return(0.0);
 };
number = sign * number;
if (op == GT && number > min) return(number);
else if (op == GE && number >= min) return(number);
else
 {
  sprintf(outstr,"erroneous value: %f",number); pg(stdout,outstr);
  if (data == stdin) pg(stdout,"\n");
  else {sprintf(outstr," in %c command\n",command); pg(stdout,outstr);};
  readerror = 1;
  return(0.0);
 };
}

WTTYPE rdr(int op,REAL min,int command)
/* reads REAL real numbers and converts */
/* them to 16-bit integers if necessary */

{
REAL x;
WTTYPE ix;
 
x = readreal(op,min,command);
if (readerror) return(0);
ix = scale(x);
if (readerror) return(0);
return(ix);
}

REAL readchar()   /* reads data in compressed format */
{
int ch2;
readerror = 0;
ch2 = readch();
do {
          switch (ch2) {
case '\n':
case ' ': ch2 = readch();
          break;
case '1': return(1.0);
case '0': return(0.0);
case 'x': return(unscale(unknown));
case '*': do ch2 = readch(); while(ch2 != '\n');
          break;
case EOF: readerror = 2;
          return(0.0);
case '}': readerror = 3;
          return(0.0);
default:  texterror();
          readerror = 1;
          return(0.0);};
} while (0 == 0);
}

void readapat(PATNODE *pn,int relation,REAL minimum,int command,char informat)
{
  REAL val;
  int unitnumber;
  UNIT *u;
  if (informat == 'r') val = rdr(relation,minimum,command);
  else val = scale(readchar());
  pn->addr = NULL;
  pn->val = val;
  pn->layer = 0;   /* unused */
  pn->unitno = 0;  /* unused */
}

void readpatson(LAYER *layer,int list,int command)

{PATLIST *pl, *prev;
 PATNODE *pn;
 int i, answer, veclength;

pl = (PATLIST *) malloc(sizeof(PATLIST));
pl->next = NULL;
pl->pats = NULL;     /* no patterns read yet */
if (layer->patstart[list] == NULL)
 {
  prev = NULL;
  layer->patstart[list] = pl;
 }
else
 {
  prev = layer->currentpat[list];
  prev->next = pl;
 };
layer->currentpat[list] = pl;

veclength = layer->unitcount;
pn = (PATNODE *) malloc(veclength * sizeof(PATNODE));
pl->pats = pn;
if (layer == last && probtype == 'c')
 {
  answer = readint(1,veclength,command);
  if (readerror != 0) return;
  for (i=1;i<=veclength;i++)
   {
    if (i == answer) pn->val = classon; else pn->val = classoff;
    pn->addr = NULL;
    pn->layer = nlayers;
    pn->unitno = i;
    pn++;
   };
  return;
 };
for (i=1;i<=veclength;i++)
 {
  readapat(pn,GE,(REAL) -MAXINT,command,informat);
  if ((readerror == 3 || readerror == 2) && i == 1 && layer == start)
   {
    if (prev != NULL) prev->next = NULL;
    free(pn);
    free(pl);
    readerror = readerror + 2;
    return;
   }
  else if (readerror != 0) return;
  pn++;
 };
return;
}

int readpats(int list,int command)
{ int i, j;
  PATLIST *pl;

readthepatterns:
i = 0;
readerror = 0;
while (readerror == 0)
 {
  readpatson(start,list,command);
  if (readerror) break;
  readpatson(last,list,command);
  if (readerror) break;
  i = i + 1;
 };
if (readerror == 5) return(i);
else if (readerror == 4)
 {
  popfile();
  return(i);
 };

/* failure for some reason */

sprintf(outstr,"error while reading pattern %d",i+1); pg(stdout,outstr);
if (readerror == 1) pg(stdout,"\n");
else if (readerror == 2) pg(stdout,":  unexpected end of file\n");
else if (readerror == 3) pg(stdout,":  unexpected }\n");
if (readerror == 2) popfile();
if (i > 0)
 {
  pl = (PATLIST *) start->patstart[list];
  for (j=1;j<=prevnpats + i - 1;j++) pl = pl->next;
  pl->next = NULL;
  pl = (PATLIST *) last->patstart[list];
  for (j=1;j<=prevnpats + i - 1;j++) pl = pl->next;
  pl->next = NULL;
 };
return(i);
}

int pg(FILE *f,char *str) // paging and making a copy function
{
char *ch3,action,cr;
int i;
long clockbefore, clockafter;
int nbackspaces = 5;

if (f != stdout) {fprintf(f,"%s",str); return(0);};
clockbefore = clock();
action = 0;
ch3 = str;
while (*ch3 != '\0')
 {
  if (*ch3 == '\n')
   {
    putchar('\n');
    if (copyflag == '+') putc('\n',copy);
    lineno = lineno + 1;
    if (pagesize > 0 && lineno > 0 && lineno % pagesize == 0)
     {
      printf("More?");
#ifdef HOTKEYS
      action = getch();
#else
      action = getchar();
      if (action != '\n') cr = getchar();
#endif
      for(i=1;i<=nbackspaces;i++) putchar(8);
      for(i=1;i<=nbackspaces;i++) putchar(' ');
      for(i=1;i<=nbackspaces;i++) putchar(8);
      if (action == 'q') goto stoppaging;
      else if (action == 'c') lineno = -MAXINT;
#ifdef UNIX
      else if (action == '\n') lineno = lineno - 1;
#else
      else if (action == 13) lineno = lineno - 1;
#endif
      else if (action == 4) lineno = pagesize / 2;
      else lineno = 0;
     }
   }
  else {putchar(*ch3); if (copyflag == '+') putc(*ch3,copy); };
  ch3++;
 };
stoppaging:
clockafter = clock();
iotime = iotime + clockafter - clockbefore;
if (action == 'q') return(1); else return(0);
}

void printnetsize(FILE *f) /* the size of the network */
{LAYER *p;
pg(f,"m ");
p = start;
while (p != NULL)
 {
  sprintf(outstr," %1d",p->unitcount);
  pg(f,outstr);
  p = p->next;
 };
}

void listseeds(FILE *f)
{
 SEEDNODE *s;
 s = seedstart->next;
 pg(f,"s");
 while (s != NULL)
  {
   sprintf(outstr," %1d",s->val); pg(f,outstr);
   s = s->next;
  };
}

int printoutunits(int patnum,LAYER *layer,int list,REAL error,char tok,char mok)
{
short unitno, fmtbreaknum;
UNIT *u;
WTTYPE upper, middle, diff;

int i;
WTTYPE *target;
PATLIST *pl;
#ifdef INTEGER
float tempreal;
#endif

upper = scale(1.0) - toler;
middle = scale(0.5);
unitno = 0;
fmtbreaknum = 1;
/*
if (layer == last && patnum > 0)
 {
  pl = last->currentpat[list];
  target = pl->pats;
  u = (UNIT *) layer->units;
  for (i=1;i<=last->unitcount;i++)
   {
    u->tj = *target++;
    u = u->next;
   };
 };
*/
if (patnum) {sprintf(outstr,"%5d ",patnum); pg(stdout,outstr);}
else pg(stdout,"      ");
u = (UNIT *) layer->units;
while (u != NULL)
 {
  diff = u->tj - u->oj;
  unitno = unitno + 1;
  if (outformat == 'r' || outformat == 's')
   {
    sprintf(outstr,"%6.3f ",unscale(u->oj)); pg(stdout,outstr);
    if (format[fmtbreaknum] == unitno)
     {
      if (pg(stdout,"\n      ")) return(1);
      if (fmtbreaknum < MAXFORMAT - 1) fmtbreaknum = fmtbreaknum + 1;
     }
   }
  else if (outformat == 'e')
   {
    sprintf(outstr,"%12.4e ",unscale(u->oj)); pg(stdout,outstr);
    if (format[fmtbreaknum] == unitno)
     {
      if (pg(stdout,"\n      ")) return(1);
      if (fmtbreaknum < MAXFORMAT - 1) fmtbreaknum = fmtbreaknum + 1;
     }
   }
  else if (outformat == 'a')
   {
    if (diff < toler) pg(stdout,"c");
    else if (u->oj > upper) pg(stdout,"1");
    else if (u->oj < toler) pg(stdout,"0");
    else if (u->oj > u->tj) pg(stdout,"^");
    else pg(stdout,"v");
    if (format[fmtbreaknum] == unitno)
     {
      pg(stdout," ");
      if (fmtbreaknum < MAXFORMAT - 1) fmtbreaknum = fmtbreaknum + 1;
     }
   }
  else 
   {
    if (u->oj > upper) pg(stdout,"1");
    else if (u->oj > middle) pg(stdout,"^");
    else if (u->oj < toler) pg(stdout,"0");
    else pg(stdout,"v");
    if (format[fmtbreaknum] == unitno)
     {
      pg(stdout," ");
      if (fmtbreaknum < MAXFORMAT - 1) fmtbreaknum = fmtbreaknum + 1;
     }
   }
  u = u->next;
 };
if (patnum > 0)
 {
  sprintf(outstr," e %5.3f",error); pg(stdout,outstr);
  if (tok) pg(stdout," ok"); else pg(stdout,"   ");
  if (mok) pg(stdout," ok");
  if (pg(stdout,"\n")) return(1); else return(0);
 };
if (pg(stdout,"\n")) return(1); 
if (pg(stdout,"\n")) return(1); else return(0);
}

void saveweights()  /* saves weights on the file weights */
{
UNIT *u, *bu;
LAYER *layer;
WTNODE *w;
WTTYPE wvalue, evalue, dvalue, svalue;
int wtsize, unitno, layerno, i;
short wtinuse;
FILE *wtfileptr;

wtsize = WTSIZE;
sprintf(wtfilename,"%s",wtfile);
wtfileptr = fopen(wtfilename,"wb");
if (wtfileptr == NULL)
 {
  sprintf(outstr,"cannot open: %s\n",wtfilename); pg(stdout,outstr);
  return;
 };
fprintf(wtfileptr,"%d%c",totaliter,wtformat);
layer = start;
fprintf(wtfileptr,"  m %1d",layer->unitcount);
layer = layer->next;
while (layer != NULL)
 {
  fprintf(wtfileptr," %1d",layer->unitcount);
  layer = layer->next;
 };
fprintf(wtfileptr,"  file = %s",datafile);
#ifdef DOSMOD   /* I don't remember this */
fputc('\r',wtfileptr);
#endif
fputc('\n',wtfileptr);

layer = start->next;
while (layer != NULL)
 {
  u = (UNIT *) layer->units;
  while (u != NULL)
   {
    w = (WTNODE *) u->wtlist;
    while (w->next != NULL)
     {
      wvalue = w->weight;
      if (wtformat == 'r')
       {
        fprintf(wtfileptr,"%14.6e",unscale(wvalue));
        bu = w->backunit;
        fprintf(wtfileptr,"  %1d ",bu->layernumber);
        if (bu->unitnumber == 32767) fprintf(wtfileptr,"b to ");
        else fprintf(wtfileptr,"%1d to ",bu->unitnumber);
        fprintf(wtfileptr,"%1d %1d",u->layernumber,u->unitnumber);
#ifdef DOSMOD
        fputc('\r',wtfileptr);
#endif
        fputc('\n',wtfileptr);
       }
      w = w->next;
     };
    u = u->next;
   };
  layer = layer->next;
 };
fflush(wtfileptr);
fclose(wtfileptr);
lastsave = totaliter;
}

void restoreweights()    /* restore weights */
{
FILE *wtfileptr;
UNIT *u;
LAYER *layer;
WTNODE *w;
int ch2, fileformat, wtsize, numread, i, layerno, unitno;
WTTYPE wvalue, evalue, dvalue, svalue;
int wtinuse;
double temp;
unsigned char *cptr;

wtfileptr = fopen(wtfilename,"rb");
if (wtfileptr == NULL)
 {
  sprintf(outstr,"cannot open: %s\n",wtfilename);
  pg(stdout,outstr);
  return;
 };
numread = fscanf(wtfileptr,"%d",&totaliter);
if (numread == EOF) goto unexpectedeof;
if (numread == 0) goto badread;
fileformat = fgetc(wtfileptr);
if (fileformat == EOF) goto unexpectedeof;
if (fileformat != wtformat) pg(stdout,"note: weight format mismatch\n");
wtsize = WTSIZE;
do ch2 = fgetc(wtfileptr); while (ch2 != '\n');
layer = start->next;
while (layer != NULL)
 {
  u = (UNIT *) layer->units;
  while (u != NULL)
   {
    w = (WTNODE *) u->wtlist;
    while (w->next != NULL)
     {
      numread = fscanf(wtfileptr,"%lf",&temp);
      if (numread == EOF) goto unexpectedeof;
      if (numread == 0) goto badread;
      wvalue = scale((REAL) temp);
      do ch2 = fgetc(wtfileptr); while (ch2 != '\n');
      if (ch2 == '\n')
       {
        ch2 = fgetc(wtfileptr);
        if (ch2 != '\r') ungetc(ch2,wtfileptr);
       };
      w->weight = wvalue;
      w = w->next;
     };
    u = u->next;
   };
  layer = layer->next;
 };
fclose(wtfileptr);
lastsave = totaliter;
return;
badread: pg(stdout,"\n>>>>> scanf read error <<<<<\n\n");
fclose(wtfileptr);
return;
unexpectedeof: pg(stdout,"\n>>>>> ran out of weights <<<<<\n\n");
fclose(wtfileptr);
}

void printweights(UNIT *u,int layerno)
/* print the weights leading into unit u */

{
WTNODE *w;
UNIT *bunit;
WTTYPE value;
LAYER *layer;
int i;
REAL sum, input, diff;

w = (WTNODE *) u->wtlist;
sum = 0;
pg(stdout,"layer unit  unit value    weight    input from unit\n");
while (w->next != NULL)
 {
  bunit = (UNIT *) w->backunit;
  if (layerno == 3) input = w->weight * bunit->oj;
  else
   {
    diff = w->weight - bunit->oj;
    input = diff * diff;
   };
  sum = sum + input;
  sprintf(outstr,"%3d   ",bunit->layernumber); pg(stdout,outstr);
  sprintf(outstr,"%3d  ",bunit->unitnumber); pg(stdout,outstr);
  sprintf(outstr,"%10.5f  %10.5f  ",bunit->oj,w->weight);
  pg(stdout,outstr);
  sprintf(outstr,"%14.5f\n",input);
  if (pg(stdout,outstr)) return;
  w = w->next;
 };
pg(stdout,"                                            ");
if (layerno == 2) sprintf(outstr,"sum = %9.5f\n",sum);
else sprintf(outstr,"sum = %9.5f\n",sum);
if (pg(stdout,outstr)) return;
pg(stdout,"\n");
}

void parameters(FILE *f)
{int i;
printnetsize(f);
sprintf(outstr," * %1d weights; ",wttotal); pg(f,outstr);
sprintf(outstr,"data file = %s  ",datafile); pg(f,outstr);
listseeds(f); pg(f,"\n");
sprintf(outstr,"r %1d %1d ",maxiter,printrate); pg(f,outstr);
if (f != stdout) pg(f,"\"\n"); else pg(f,"\n");
sprintf(outstr,"f b%c c%c e%c i%c",ringbell,copyflag,echo,informat);
pg(f,outstr);
sprintf(outstr," o%c P %d p%c ",outformat,pagesize,probtype);
pg(f,outstr);
sprintf(outstr,"R%c ",runningflag); pg(f,outstr);
sprintf(outstr," s%c ",summary); pg(f,outstr);
sprintf(outstr,"x% f\n",unscale(unknown)); pg(f,outstr);
pg(f,"f B ");
for (i=1;i<=MAXFORMAT-1;i++)
 {sprintf(outstr," %1d",format[i]); pg(f,outstr);};
pg(f,"\n");
sprintf(outstr,"! %s ",sysstr); pg(f,outstr);
if (f != stdout) pg(f,"\"\n"); else pg(f,"\n");
sprintf(outstr,"e %7.5f %7.5f\n",unscale(eta),unscale(eta2));pg(f,outstr);
sprintf(outstr,"* last save at: %d\n",lastsave); pg(f,outstr);
sprintf(outstr,"rw %s\n",wtfilename); pg(f,outstr);
sprintf(outstr,"t %4.2f\n",unscale(toler)); pg(f,outstr);

printstats(f,TRAIN,1,s);
if (s[TOL][TEST].npats > 0) printstats(f,TEST,1,s);
pg(f,"\n");
}

void menus(char ch)
{
switch (ch) {

case '?': parameters(stdout); return;

case 'A':
pg(stdout,"\nAlgorithm Parameters\n\n");
sprintf(outstr,"t %4.3f        ",unscale(toler)); pg(stdout,outstr);
pg(stdout,"tolerance/unit for successful learning; (0..1)\n");
sprintf(outstr,"e %5.3f %5.3f  ",eta, eta2); pg(stdout,outstr);
pg(stdout,"upper level eta and lower level eta; [0..inf)\n");
pg(stdout,"\n");
return;

case 'C':
pg(stdout,"\nScreen     Includes Information and Parameters on:\n\n");
pg(stdout,"  A        algorithm parameters (tolerance and learning rates)\n");
pg(stdout,"  C        this listing of major command groups\n");
pg(stdout,"  F        formats: patterns, output, paging, copying screen i/o\n");
pg(stdout,"  M        miscellaneous commands: shell escape, seed values, clear,\n");
pg(stdout,"           clear and initialize, quit\n");
pg(stdout,"  N        making a network, listing network unit values\n");
pg(stdout,"  P        pattern commands: reading patterns, testing patterns,\n");
pg(stdout,"  T        a short tutorial\n");
pg(stdout,"  W        weight commands: listing, saving, restoring, set bias unit weights\n");
pg(stdout,"  ?        a compact listing of everything\n");
pg(stdout,"\n");
return;


case 'F':
pg(stdout,"\nThe Format Command (f)\n\n");
sprintf(outstr,"f b %c         ",ringbell); pg(stdout,outstr);
pg(stdout,"ring the bell when training is finished; {+-}\n");
sprintf(outstr,"f c %c         ",copyflag); pg(stdout,outstr);
pg(stdout,"make a copy of screen i/o on the file copy; {+-}\n");
sprintf(outstr,"f e %c         ",echo); pg(stdout,outstr);
pg(stdout,"echo input; {+-}\n");
sprintf(outstr,"f i %c         ",informat); pg(stdout,outstr);
pg(stdout,"format for reading input and output patterns; {cr}         h fi\n");
sprintf(outstr,"f o %c         ",outformat); pg(stdout,outstr);
pg(stdout,"format for outputing network's unit values; {acer}         h fo\n");
sprintf(outstr,"f P %2d        ",pagesize); pg(stdout,outstr);
pg(stdout,"lines per page; 0 = no paging; [0..inf)\n");
pg(stdout,"f P <int>     ");
pg(stdout,"lines per page; 0 = no paging; [0..inf)\n");
sprintf(outstr,"f p %c         ",probtype); pg(stdout,outstr);
pg(stdout,"problem type; {cg}                                         h fp\n");
sprintf(outstr,"f R %c         ",runningflag); pg(stdout,outstr);
pg(stdout,"print the \"running . . .\" message; {+-}\n");
sprintf(outstr,"f s %c         ",summary); pg(stdout,outstr);
pg(stdout,"summarize learning status while running; {+-}\n");
sprintf(outstr,"f x %5.2f     ",unknown); pg(stdout,outstr);
pg(stdout,"the value of x in patterns\n");
pg(stdout,"f B ");
{int i;
for (i=1;i<=MAXFORMAT-1;i++)
 {sprintf(outstr," %1d",format[i]); pg(stdout,outstr);};};
pg(stdout,"\n                            ");
pg(stdout,"                                             h fB\n");
pg(stdout,"\n");
return;


case 'M':
pg(stdout,"\nMiscellaneous Commands\n\n");

if (sysstr[0] != '\0')
   {pg(stdout,"!               "); pg(stdout,sysstr); pg(stdout,"\n");};
pg(stdout,"! <string>      pass <string> to the operating system\n\n");

pg(stdout,"q               q by itself quits the program\n\n");

pg(stdout,"c               clear the network weights\n");
pg(stdout,"ci              same as c except the weights are initialized to\n");
sprintf(outstr,"                between -%4.2f ",unscale(initrange));
pg(stdout,outstr);
sprintf(outstr,"and %4.2f\n",unscale(initrange)); pg(stdout,outstr);
pg(stdout,"ci <real>       same as ci except the weights are initialized to\n");
pg(stdout,"                between - <real> and + <real>\n\n");

sprintf(outstr,"r               run %d iterations",maxiter); pg(stdout,outstr);
pg(stdout," and print a summary every\n");
sprintf(outstr,"                %d iterations\n",printrate); pg(stdout,outstr);
pg(stdout,"^R              same as r\n");
pg(stdout,"r <int1> <int2> run <int1> iterations and print a summary every\n");
pg(stdout,"                <int2> iterations\n");

sprintf(outstr,"s %5d",seed); pg(stdout,outstr);
sprintf(outstr,"         random number seed: %1d\n",seed); pg(stdout,outstr);
pg(stdout,"\n");
return;

case 'N':
pg(stdout,"\nNetwork Information\n\n");
printnetsize(stdout);
pg(stdout,"\n");
sprintf(outstr,"                 total weights: %1d\n",wttotal);
pg(stdout,outstr);

pg(stdout,"l <layer>        print the unit values in layer <layer>\n\n");
return;

case 'P':
pg(stdout,"\nPattern Related Commands\n\n");
pg(stdout,"f p g          the problem type; {gc}                             h fp\n");
pg(stdout,"f i r          format to input patterns; {cr}                     h fi\n");
pg(stdout,"f o r          format to output network values; {acer}            h fo\n\n");

pg(stdout,"rt {patterns}  reads the training patterns between { and }\n");
pg(stdout,"rt <filename>  read training patterns from <filename>\n");
pg(stdout,"tf {patterns}  reads the test set patterns between { and }\n");
pg(stdout,"tf <filename>  test file is <filename>\n\n");

pg(stdout,"p              list all training set pattern values\n");
pg(stdout,"pa             same as p\n");
pg(stdout,"p <pat. no.>   evaluate the training pattern\n");
pg(stdout,"p0             summarize training pattern learning\n\n");

pg(stdout,"t              list all test set pattern values\n");
pg(stdout,"ta             same as t\n");
pg(stdout,"t <pat. no.>   evaluate the test pattern\n");
pg(stdout,"t0             summarize test pattern learning\n\n");

pg(stdout,"o <int>        prints the target for training pattern <int>\n\n");

printstats(stdout,TRAIN,1,s);
if (s[TOL][TEST].npats > 0) printstats(stdout,TEST,1,s);
pg(stdout,"\n");
return;

case 'T':
pg(stdout,"\nA Tutorial\n\n");
pg(stdout,"The following topics are designed to be read in the order listed.\n\n");
pg(stdout,"To get help on a topic type the code on the right at the prompt.\n\n");
pg(stdout,"Understanding the Menus                             h1\n");
pg(stdout,"Formatting Data for a Classification Problem        h2\n");
pg(stdout,"Formatting Data for Function Approximation          h3\n");
pg(stdout,"Making a Network                                    h4\n");
pg(stdout,"Reading the Data                                    h5\n");
pg(stdout,"Initializing the Network and Running the Program    h6\n"); 
pg(stdout,"Command Files                                       h7\n");
pg(stdout,"To Quit the Program                                 h8\n\n");
return;

case 'W':
pg(stdout,"\nWeights Related Commands\n\n");
pg(stdout,"rw               reads weights from the file "); pg(stdout,wtfilename);
pg(stdout,"\n");
pg(stdout,"sw               saves weights to the file "); pg(stdout,wtfilename);
pg(stdout,"\n");
pg(stdout,"rw <filename>    reads weights from <filename>\n");
pg(stdout,"sw <filename>    save weights to <filename>\n");
sprintf(outstr,"                 last save at: %1d\n\n",lastsave);
pg(stdout,outstr);

pg(stdout,"w <layer> <unit> list weights leading into this unit\n\n");

printnetsize(stdout);
pg(stdout,"\n");
sprintf(outstr,"                 total weights: %1d\n",wttotal);
pg(stdout,outstr);
pg(stdout,"\n");
return;

default: pg(stdout,"not a screen item\n");
return;
}; /* end switch */
}

void help()
{
int ch2;
pg(stdout,"\n");
do ch2 = readch(); while (ch2 == ' ' && ch2 != '\n');
        switch(ch2) {

default: menus('C'); return;

case '1':
bufferptr = bufferptr - 1;
pg(stdout,"Understanding the Menus\n\n");
pg(stdout,"First, this program includes a paging algorithm similar to more.  When\n");
pg(stdout,"you get the string:\n");
pg(stdout,"\n");
pg(stdout,"More?\n");
pg(stdout,"\n");
pg(stdout,"at the bottom of the screen you can get one more page by hitting the\n");
pg(stdout,"space bar, one more line by hitting the carriage return, half a page\n");
pg(stdout,"with ctrl-D, a c will continue without paging and in most cases typing\n");
pg(stdout,"a q will stop the paging.  There is no scrolling backwards.\n\n");
pg(stdout,"While the program does not use a mouse or windows it does have menus to\n");
pg(stdout,"make it easy to see what the commands are.  One typical menu is the F\n");
pg(stdout,"for format menu, so if you type F at the command prompt you get:\n\n");
pg(stdout,"The Format Command (f)\n\n");
pg(stdout,"f b -         ring the bell when training is finished; {+-}\n");
pg(stdout,"f c +         make a copy of screen i/o on the file copy; {+-}\n");
pg(stdout,"f e -         echo input; {+-}\n");
pg(stdout,"f i r         format for reading input and output patterns; {cr}         h fi\n\n");
pg(stdout,"plus there is more that has been deleted but these four lines show enough.\n");
pg(stdout,"The first column gives the command and the current value of the parameter.\n");
pg(stdout,"In the `f b -' string, the `f' is the general format command that\n");
pg(stdout,"includes many sub-commands.  The `b' sub-command controls the ringing\n");
pg(stdout,"of the bell when the program finishes learning.  With the `-' it is off.\n");
pg(stdout,"The middle portion of the screen gives the function involved with the\n");
pg(stdout,"command.  Characters at the end within { and } list the possible options.\n");
pg(stdout,"For ringing the bell it is on or off, `+' or `-'.  In the `f i r' the\n");
pg(stdout,"explanation for c and r within the { and } can be found by typing `h f i'\n");
pg(stdout,"at the normal command prompt.  To change the settings you only have to\n");
pg(stdout,"type the command string in the left column but with the new parameter\n");
pg(stdout,"setting.\n\n");
pg(stdout,"In many cases the command parameters are numbers.  The range of values\n");
pg(stdout,"that the parameter can take on are given using [, ], (, and ).  If the\n");
pg(stdout,"range listed is: [0..1) the [ next to 0 means that the lowest possible\n");
pg(stdout,"value allowed is exactly 0 while the ) indicates that you can get very\n");
pg(stdout,"close to 1 but never exactly 1.\n\n");
pg(stdout,"In some cases the leftmost column will code a possible command parameter\n");
pg(stdout,"using some name within < and >.  For instance there are these two\n");
pg(stdout,"examples:\n\n");
pg(stdout,"e <real1> <real2>\n");
pg(stdout,"sw <filename>    save weights to <filename>\n");
pg(stdout,"\n");
pg(stdout,"In the first case it means that to set the learning rate parameters\n");
pg(stdout,"to some values <real1> and <real2>.  To do this you type `e' and some real number as in:\n\n");
pg(stdout,"e 0 0.7\n\n");
pg(stdout,"In the second example the command means save the weight to some filename\n");
pg(stdout,"so type the name in place of `<filename>'.\n\n");
return;

case '2':
pg(stdout,"The normal way to operate this program is to have the training data\n");
pg(stdout,"in one file and if there is test set data it should be in another file.\n\n");
pg(stdout,"For a plain classification program, with say four inputs and 3 possible\n");
pg(stdout,"classes the data file will look like:\n\n");
pg(stdout,"0.4  0.3   0.33 0.21    1  * this is a class 1 pattern\n");
pg(stdout,"0.55 0.32 -0.09 0.20    2  * this is a class 2 pattern\n");
pg(stdout,"0.11 0.23 -0.97 0.45    3  * this is a class 3 pattern\n\n");
pg(stdout,"where the first four numbers are the input and the last number is the\n");
pg(stdout,"class number.  If there are a large number of inputs you can use more\n");
pg(stdout,"than one line for each pattern but you should start every new pattern\n");
pg(stdout,"on a new line.  The asterisk at the end of the line begins a comment.\n\n");
pg(stdout,"It is also possible to type the data in as commands but this is\n");
pg(stdout,"generally not a good idea unless you have a very small data set\n");
pg(stdout,"because you will probably make many mistakes when typing the data\n");
pg(stdout,"in.\n\n");
return;

case '3':
pg(stdout,"The normal way to operate this program is to have the training data\n");
pg(stdout,"in one file and if there is test set data it should be in another file.\n\n");
pg(stdout,"For a function approximation program with say five inputs and 1 output\n");
pg(stdout,"the data should look like:\n\n");
pg(stdout,"  -1.588   -1.650     0.365     0.188     0.962   -1.543\n");
pg(stdout,"  -1.182   -0.926     0.992     0.188     1.140   -1.372\n");
pg(stdout,"  -2.650   -1.650     3.501     0.188    -0.566   -1.201\n\n");
pg(stdout,"where the first five numbers are the input and the last number is the\n");
pg(stdout,"answer.  If there are a large number of inputs you can use more\n");
pg(stdout,"than one line for each pattern but you should start every new pattern\n");
pg(stdout,"on a new line.\n\n");
pg(stdout,"It is also possible to type the data in as commands but this is\n");
pg(stdout,"generally not a good idea unless you have a very small data set because\n");
pg(stdout,"you will probably make many mistakes when typing the data in.\n\n");
return;

case '4':
pg(stdout,"The command to type in to make a network with four inputs, three\n");
pg(stdout,"hidden layer units and two output units is:\n\n");
pg(stdout,"m 4 3 2\n\n");
pg(stdout,"NOTE: there is no way to know ahead of time how many hidden units will\n");
pg(stdout,"be needed.\n\n");
pg(stdout,"NOTE: when patterns are read in they are attached to the network\n");
pg(stdout,"structure so if you make a different size network, such as a 4-5-2\n");
pg(stdout,"network after the patterns are read in the patterns will be lost and\n");
pg(stdout,"you will have to read them in again.\n\n");
return;

case '5':
pg(stdout,"If you're doing a classification type problem it is important to first\n");
pg(stdout,"let the program know that using the `f' (format) command so the last\n");
pg(stdout,"number can be interpreted as the class number, use:\n\n");
pg(stdout,"f p c   * the problem type is classification\n\n");
pg(stdout,"If you're doing a function approximation type of problem, do nothing\n");
pg(stdout,"since the default setting (g, for general) will work.\n\n");
pg(stdout,"To read in the training set data from some file called, say train is:\n\n");
pg(stdout,"rt train   * rt is for read training\n\n\n");
pg(stdout,"To read in any test set data from some file called, say, test is:\n\n");
pg(stdout,"tf test    * tf is for test file\n\n\n");
pg(stdout,"For other ways to read in data see the manual.\n\n");
return;

case '6':
pg(stdout,"\n");
pg(stdout,"Normally you want to initialize the weights in the network with small random\n");
pg(stdout,"values.  This is done with the clear and initialize command, type in:\n");
pg(stdout,"\n");
pg(stdout,"ci   * ci for clear and initialize\n");
pg(stdout,"\n");
pg(stdout,"Having done that you can run the training program by typing `r' and a\n");
pg(stdout,"carriage return or by typing ctrl-R.  Doing this applies the training\n");
pg(stdout,"algorithm for up to 100 passes through the training set data and lists\n");
pg(stdout,"the status of the training set and the test set if it exists.  For the\n");
pg(stdout,"sonar data included in the sample data the listing will look like:\n");
pg(stdout,"\n");
pg(stdout,"   10     49.04 %  49.04 % 0.47063      62.50 %  62.50 % 0.38221 \n");
pg(stdout,"   20     70.19 %  73.08 % 0.38548      77.88 %  77.88 % 0.38063 \n");
pg(stdout,"   30     76.92 %  76.92 % 0.34943      77.88 %  80.77 % 0.33282 \n");
pg(stdout,"\n");
pg(stdout,"The first column is the iteration number, the second column gives the\n");
pg(stdout,"percentage of training set patterns right based on the tolerance the\n");
pg(stdout,"third column gives the percentage right based on the maximum value, the\n");
pg(stdout,"fourth column gives the abs (not RMS!) error.  The fifth column is the\n");
pg(stdout,"percentage of test set patterns right based on the tolerance, the sixth\n");
pg(stdout,"column gives the percentage right based on maximum value and the last\n");
pg(stdout,"column gives the abs error.\n");
pg(stdout,"\n");
return;

case '7':
pg(stdout,"\n");
pg(stdout,"It is possible to use the program by typing in all the commands at the\n");
pg(stdout,"keyboard yet it is more convenient to have the basic set up commands for\n");
pg(stdout,"a particular problem in a file, have the program read the file and then\n");
pg(stdout,"have it take commands from the keyboard.  So if you have a file named\n");
pg(stdout,"xor set up you can start the program with:\n");
pg(stdout,"\n");
pg(stdout,"bp xor\n");
pg(stdout,"\n");
return;

case '8':
pg(stdout,"In DOS and UNIX type `q' at the command line prompt.\n\n");
pg(stdout,"In DOS you can hit the escape key to stop a training run.\n\n");
pg(stdout,"In DOS and UNIX you can hit ctrl-C to stop anything and get to the\n");
pg(stdout,"command prompt.  When used in a DOS window under Windows this may\n");
pg(stdout,"produce an error message as well as stopping the program.\n\n");
return;

case 'f':
do ch2 = readch(); while (ch2 == ' ');
if (ch2 == 'B')
 {
pg(stdout,"B <int1> <int2> ... <inti> ... <int20> puts a carriage return\n");
pg(stdout,"          after each <inti> values when the output format is real\n");
pg(stdout,"          and inserts a blank after each <inti> value if the\n");
pg(stdout,"          format is condensed.\n\n");
 }
else if (ch2 == 'i')
 {
  pg(stdout,"i c will read pattern values using compressed format.\n");
  pg(stdout,"i r will read pattern values as reals.\n\n");
 }
else if (ch2 == 'o')
 {
  pg(stdout,"o a will write node values as analog compressed.\n");
  pg(stdout,"o c will write node values as compressed.\n");
  pg(stdout,"o e will write node values with e notation.\n");
  pg(stdout,"o r will write node values as real.\n\n");
 }
else if (ch2 == 'p')
 {
  pg(stdout,"p c will read patterns in the classification format.\n");
  pg(stdout,"p g will accept general patterns.\n\n");
 }
else texterror();
break;

     }; /* end switch */
pg(stdout,"\n");
}
